/*
 * @Author: 张宇飞
 * @Date: 2020-02-20 23:18:47
 * @LastEditors: 张宇飞
 * @LastEditTime: 2020-02-21 22:14:07
 * @Description: 演示 mutex 的例子。mutex
 * 是互斥锁。其特点是被加锁的资源在同一时刻只能被一个线程所访问。
 * 但是它有个特点，就是不加锁也可以访问。所以它是系统的建议锁。没有强制的规定必须加锁才能访问。
 * @Email: zhangyufei49@163.com
 */

#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

#include "utils.h"
static int shared_val = 10;
// 静态分配锁的时候常用 PTHREAD_MUTEX_INITIALIZER 这个宏定义。
// 动态分配的时候会使用 pthread_mutex_init 并且可以指定锁的一些属性
static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t mutex2;

static void* func1(void* arg) {
    fprintf(stderr, "in thread 1 lock mutex2 at the 1st time return: %d\n",
            pthread_mutex_lock(&mutex2));
    // metex2 初始化的时候配置了类型为
    // PTHREAD_MUTEX_ERRORCHECK。当在同一线程内多次对同一个 mutex
    // 加锁的时候不阻塞 而是立刻返回 EDEADLK
    int r = pthread_mutex_lock(&mutex2);
    fprintf(stderr,
            "in thread 1 lock mutex2 at the 2nd time return: %d is %s\n", r,
            r == EDEADLK ? "EDEADLK" : "OTHER");
    while (shared_val > 0) {
        if (pthread_mutex_lock(&mutex1)) {
            fprintf(stderr, "thread 1 lock failed and exiting...\n");
            return NULL;
        }
        --shared_val;
        fprintf(stderr, "in thread 1 share val = %d\n", shared_val);
        pthread_mutex_unlock(&mutex1);
        sleep(1);
    }
    pthread_mutex_unlock(&mutex2);
    return NULL;
}

static void* func2(void* arg) {
    while (shared_val > 0) {
        ///* 如果解开这里面的注释就能看到。mutex 是建议锁这种特点了
        if (pthread_mutex_lock(&mutex1)) {
            fprintf(stderr, "thread 2 lock failed and exiting...\n");
            return NULL;
        }
        //*/
        --shared_val;
        fprintf(stderr, "in thread 2 share val = %d\n", shared_val);
        pthread_mutex_unlock(&mutex1);
        sleep(1);
    }
    return NULL;
}

static void* func3(void* arg) {
    // 保证 func1 已经启动并且已经对 mutex2 加锁
    sleep(1);

    // trylock 函数不像 lock 函数一样会阻塞。如果不可用，它会立刻返回 EBUSY
    if (pthread_mutex_trylock(&mutex2) == EBUSY) {
        fprintf(stderr, "in thread 3 trylock return EBUSY\n");
        return NULL;
    }
    // 正常情况下，这里的代码不会被执行
    pthread_mutex_unlock(&mutex2);

    return NULL;
}

int main(int argc, char const* argv[]) {
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
    pthread_mutex_init(&mutex2, &attr);

    run_threads(func1, func2, func3, NULL);

    pthread_mutex_destroy(&mutex2);
    pthread_mutexattr_destroy(&attr);

    return 0;
}
